msg_tool\scripts\entis_gls\csx\v1/
disasm.rs1use super::types::*;
2use crate::ext::io::*;
3use crate::types::*;
4use crate::utils::struct_pack::*;
5use anyhow::Result;
6use std::io::{Seek, Write};
7
8use CSCompareType::*;
9use CSInstructionCode::*;
10use CSObjectMode::*;
11use CSOperatorType::*;
12use CSUnaryOperatorType::*;
13use CSVariableType::*;
14
15fn escape_string(s: &str) -> String {
16 s.replace("\r", "\\r")
17 .replace("\n", "\\n")
18 .replace("\t", "\\t")
19}
20
21pub struct ECSExecutionImageDisassembler<'a> {
22 pub stream: MemReaderRef<'a>,
23 conststr: Option<&'a TaggedRefAddressList>,
24 pub assembly: ECSExecutionImageAssembly,
25 writer: Option<Box<dyn Write + 'a>>,
26 addr: u32,
27 code: CSInstructionCode,
28}
29
30impl<'a> ECSExecutionImageDisassembler<'a> {
31 pub fn new(
32 stream: MemReaderRef<'a>,
33 conststr: Option<&'a TaggedRefAddressList>,
34 writer: Option<Box<dyn Write + 'a>>,
35 ) -> Self {
36 Self {
37 stream,
38 conststr,
39 assembly: ECSExecutionImageAssembly {
40 command_list: Vec::new(),
41 },
42 writer,
43 addr: 0,
44 code: CsicNew,
45 }
46 }
47
48 pub fn execute(&mut self) -> Result<()> {
49 let len = self.stream.data.len();
50 while self.stream.pos < len {
51 self.addr = self.stream.pos as u32;
52 let code_value = self.stream.read_u8()?;
53 self.code = CSInstructionCode::try_from(code_value).map_err(|_| {
54 anyhow::anyhow!(
55 "Invalid CSInstructionCode value: {} at {:08x}",
56 code_value,
57 self.addr
58 )
59 })?;
60 match self.code {
61 CsicNew => {
62 self.command_new()?;
63 }
64 CsicFree => {
65 self.command_free()?;
66 }
67 CsicLoad => {
68 self.command_load()?;
69 }
70 CsicStore => {
71 self.command_store()?;
72 }
73 CsicEnter => {
74 self.command_enter()?;
75 }
76 CsicLeave => {
77 self.command_leave()?;
78 }
79 CsicJump => {
80 self.command_jump()?;
81 }
82 CsicCJump => {
83 self.command_cjump()?;
84 }
85 CsicCall => {
86 self.command_call()?;
87 }
88 CsicReturn => {
89 self.command_return()?;
90 }
91 CsicElement => {
92 self.command_element()?;
93 }
94 CsicElementIndirect => {
95 self.command_element_indirect()?;
96 }
97 CsicOperate => {
98 self.command_operate()?;
99 }
100 CsicUniOperate => {
101 self.command_uni_operate()?;
102 }
103 CsicCompare => {
104 self.command_compare()?;
105 }
106 }
107 let size = self.stream.pos as u32 - self.addr;
108 self.assembly
109 .command_list
110 .push(ECSExecutionImageCommandRecord {
111 code: self.code,
112 addr: self.addr,
113 size,
114 new_addr: self.addr,
115 });
116 }
117 Ok(())
118 }
119
120 fn line<S: AsRef<str> + ?Sized>(&mut self, line: &S) -> anyhow::Result<()> {
121 if let Some(writer) = &mut self.writer {
122 writeln!(writer, "{:08x} {}", self.addr, line.as_ref())?;
123 }
124 Ok(())
125 }
126
127 fn get_string_literal2(&mut self) -> Result<(Option<usize>, String)> {
128 let length = self.stream.read_u32()?;
129 if length != 0x80000000 {
130 self.stream.seek_relative(-4)?;
131 let s = WideString::unpack(&mut self.stream, false, Encoding::Utf16LE, &None)?.0;
132 Ok((None, s))
133 } else if let Some(conststr) = &self.conststr {
134 let index = self.stream.read_u32()? as usize;
135 match conststr.get(index) {
136 Some(s) => Ok((Some(index), s.tag.0.clone())),
137 None => Err(anyhow::anyhow!(
138 "Invalid string literal index: {} (max {})",
139 index,
140 conststr.len()
141 )),
142 }
143 } else {
144 Err(anyhow::anyhow!(
145 "No constant string table for string literal index"
146 ))
147 }
148 }
149
150 pub fn get_string_literal(&mut self) -> Result<String> {
151 let (_, s) = self.get_string_literal2()?;
152 Ok(s)
153 }
154
155 fn read_csct(&mut self) -> Result<CSCompareType> {
156 let value = self.stream.read_u8()?;
157 CSCompareType::try_from(value).map_err(|_| {
158 anyhow::anyhow!(
159 "Invalid CSCompareType value: {} at {:08x}",
160 value,
161 self.addr
162 )
163 })
164 }
165
166 pub fn read_csom(&mut self) -> Result<CSObjectMode> {
167 let value = self.stream.read_u8()?;
168 CSObjectMode::try_from(value).map_err(|_| {
169 anyhow::anyhow!("Invalid CSObjectMode value: {} at {:08x}", value, self.addr)
170 })
171 }
172
173 fn read_csot(&mut self) -> Result<CSOperatorType> {
174 let value = self.stream.read_u8()?;
175 CSOperatorType::try_from(value).map_err(|_| {
176 anyhow::anyhow!(
177 "Invalid CSOperatorType value: {} at {:08x}",
178 value,
179 self.addr
180 )
181 })
182 }
183
184 fn read_csuot(&mut self) -> Result<CSUnaryOperatorType> {
185 let value = self.stream.read_u8()?;
186 CSUnaryOperatorType::try_from(value).map_err(|_| {
187 anyhow::anyhow!(
188 "Invalid CSUnaryOperatorType value: {} at {:08x}",
189 value,
190 self.addr
191 )
192 })
193 }
194
195 pub fn read_csvt(&mut self) -> Result<CSVariableType> {
196 let value = self.stream.read_u8()?;
197 CSVariableType::try_from(value).map_err(|_| {
198 anyhow::anyhow!(
199 "Invalid CSVariableType value: {} at {:08x}",
200 value,
201 self.addr
202 )
203 })
204 }
205
206 fn command_new(&mut self) -> Result<()> {
207 let csom = self.read_csom()?;
208 let typ = self.read_csvt()?;
209 let class_name = if typ == CsvtObject {
210 Some(self.get_string_literal()?)
211 } else {
212 None
213 };
214 let name = self.get_string_literal()?;
215 let pobj = match csom {
216 CsomStack => "stack",
217 CsomThis => "this",
218 _ => {
219 return Err(anyhow::anyhow!(
220 "Invalid CSObjectMode for 'new' instruction: {:?} at {:08x}",
221 csom,
222 self.addr
223 ));
224 }
225 };
226 match &class_name {
227 Some(class_name) => {
228 self.line(&format!("New {pobj} \"{class_name}\" \"{name}\""))?;
229 }
230 None => {
231 self.line(&format!("New {pobj} \"{name}\""))?;
232 }
233 }
234 Ok(())
235 }
236
237 fn command_free(&mut self) -> Result<()> {
238 self.line("Free")?;
239 Ok(())
240 }
241
242 fn command_load(&mut self) -> Result<()> {
243 let csom = self.read_csom()?;
244 let csvt = self.read_csvt()?;
245 if csom == CsomImmediate {
246 match csvt {
247 CsvtObject => {
248 let class_name = self.get_string_literal()?;
249 self.line(&format!("Load * {class_name}"))?;
250 }
251 CsvtReference => {
252 self.line("Load * ECSReference")?;
253 }
254 CsvtArray => {
255 self.line("Load * ECSArray")?;
256 }
257 CsvtHash => {
258 self.line("Load * ECSHash")?;
259 }
260 CsvtInteger => {
261 let val = self.stream.read_u32()?;
262 self.line(&format!("Load Integer {}", val))?;
263 }
264 CsvtReal => {
265 let val = self.stream.read_f64()?;
266 self.line(&format!("Load Real {}", val))?;
267 }
268 CsvtString => {
269 let t = self.get_string_literal2()?;
270 let escaped = escape_string(&t.1);
271 if let Some(index) = t.0 {
272 self.line(&format!("Load Const String {index} \"{escaped}\""))?;
273 } else {
274 self.line(&format!("Load String \"{escaped}\""))?;
275 }
276 }
277 CsvtInteger64 => {
278 let val = self.stream.read_u64()?;
279 self.line(&format!("Load Integer64 {}", val))?;
280 }
281 CsvtPointer => {
282 let value = self.stream.read_u32()?;
283 self.line(&format!("Load Pointer {}", value))?;
284 }
285 }
286 } else {
287 let pobj = match csom {
288 CsomStack => "stack",
289 CsomThis => "this",
290 CsomGlobal => "global",
291 CsomData => "data",
292 CsomAuto => "auto",
293 _ => {
294 return Err(anyhow::anyhow!(
295 "Invalid CSObjectMode for 'load' instruction: {:?} at {:08x}",
296 csom,
297 self.addr
298 ));
299 }
300 };
301 match csvt {
302 CsvtReference => {
303 self.line(&format!("Load {pobj}"))?;
304 }
305 CsvtInteger => {
306 let index = self.stream.read_i32()?;
307 self.line(&format!("Load {pobj} [{index}]"))?;
308 }
309 CsvtString => {
310 let name = self.get_string_literal()?;
311 self.line(&format!("Load {pobj} [\"{name}\"]"))?;
312 }
313 _ => {
314 return Err(anyhow::anyhow!(
315 "Invalid CSVariableType for 'load' instruction: {:?} at {:08x}",
316 csvt,
317 self.addr
318 ));
319 }
320 }
321 }
322 Ok(())
323 }
324
325 fn command_store(&mut self) -> Result<()> {
326 let csot = self.read_csot()?;
327 match csot {
328 CsotNop => {
329 self.line("Store")?;
330 }
331 CsotAdd => {
332 self.line("Store Add")?;
333 }
334 CsotSub => {
335 self.line("Store Sub")?;
336 }
337 CsotMul => {
338 self.line("Store Mul")?;
339 }
340 CsotDiv => {
341 self.line("Store Div")?;
342 }
343 CsotMod => {
344 self.line("Store Mod")?;
345 }
346 CsotAnd => {
347 self.line("Store And")?;
348 }
349 CsotOr => {
350 self.line("Store Or")?;
351 }
352 CsotXor => {
353 self.line("Store Xor")?;
354 }
355 CsotLogicalAnd => {
356 self.line("Store LAnd")?;
357 }
358 CsotLogicalOr => {
359 self.line("Store LOr")?;
360 }
361 }
362 Ok(())
363 }
364
365 fn command_enter(&mut self) -> Result<()> {
366 let name = self.get_string_literal()?;
367 let num_args = self.stream.read_i32()?;
368 if num_args != -1 {
369 let mut sb = String::new();
370 sb.push('(');
371 for i in 0..num_args {
372 let csvt = self.read_csvt()?;
373 let class_name = if csvt == CsvtObject {
374 Some(self.get_string_literal()?)
375 } else {
376 None
377 };
378 let var_name = self.get_string_literal()?;
379 if let Some(cname) = class_name {
380 sb.push_str(&format!("{{{}:{}}}", cname, var_name));
381 } else {
382 sb.push_str(&var_name);
383 }
384 if i < num_args - 1 {
385 sb.push_str(", ");
386 }
387 }
388 sb.push(')');
389 self.line(&format!("Enter \"{}\" {}", name, sb))?;
390 } else {
391 let flag = self.stream.read_u8()?;
392 if flag != 0 {
393 return Err(anyhow::anyhow!(
394 "Invalid flag for variable argument 'enter' instruction: {} at {:08x}",
395 flag,
396 self.addr
397 ));
398 }
399 let catch_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
400 self.line(&format!("Enter \"{}\" Try-Catch {:08x}", name, catch_addr))?;
401 }
402 Ok(())
403 }
404
405 fn command_leave(&mut self) -> Result<()> {
406 self.line("Leave")?;
407 Ok(())
408 }
409
410 fn command_jump(&mut self) -> Result<()> {
411 let target_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
412 self.line(&format!("Jump {:08x}", target_addr))?;
413 Ok(())
414 }
415
416 fn command_cjump(&mut self) -> Result<()> {
417 let cond = self.stream.read_u8()?;
418 let target_addr = self.stream.read_i32()? as i64 + self.stream.pos as i64;
419 self.line(&format!("CJump {} {:08x}", cond, target_addr))?;
420 Ok(())
421 }
422
423 fn command_call(&mut self) -> Result<()> {
424 let csom = self.read_csom()?;
425 let num_args = self.stream.read_i32()?;
426 let func_name = self.get_string_literal()?;
427 let pobj = match csom {
428 CsomImmediate if func_name == "@CATCH" => "",
429 CsomThis => "this",
430 CsomGlobal => "global",
431 CsomAuto => "auto",
432 _ => {
433 return Err(anyhow::anyhow!(
434 "Invalid CSObjectMode for 'call' instruction: {:?} at {:08x}",
435 csom,
436 self.addr
437 ));
438 }
439 };
440 self.line(&format!("Call {} \"{}\" <{}>", pobj, func_name, num_args))?;
441 Ok(())
442 }
443
444 fn command_return(&mut self) -> Result<()> {
445 let free_stack = self.stream.read_u8()?;
446 self.line(&format!("Return {}", free_stack))?;
447 Ok(())
448 }
449
450 fn command_element(&mut self) -> Result<()> {
451 let csvt = self.read_csvt()?;
452 match csvt {
453 CsvtInteger => {
454 let index = self.stream.read_i32()?;
455 self.line(&format!("Element {}", index))?;
456 }
457 CsvtString => {
458 let name = self.get_string_literal()?;
459 self.line(&format!("Element \"{}\"", name))?;
460 }
461 _ => {
462 return Err(anyhow::anyhow!(
463 "Invalid CSVariableType for 'element' instruction: {:?} at {:08x}",
464 csvt,
465 self.addr
466 ));
467 }
468 }
469 Ok(())
470 }
471
472 fn command_element_indirect(&mut self) -> Result<()> {
473 self.line("ElementIndirect")?;
474 Ok(())
475 }
476
477 fn command_operate(&mut self) -> Result<()> {
478 let csot = self.read_csot()?;
479 match csot {
480 CsotNop => {
481 self.line("Operate Nop")?;
482 }
483 CsotAdd => {
484 self.line("Operate Add")?;
485 }
486 CsotSub => {
487 self.line("Operate Sub")?;
488 }
489 CsotMul => {
490 self.line("Operate Mul")?;
491 }
492 CsotDiv => {
493 self.line("Operate Div")?;
494 }
495 CsotMod => {
496 self.line("Operate Mod")?;
497 }
498 CsotAnd => {
499 self.line("Operate And")?;
500 }
501 CsotOr => {
502 self.line("Operate Or")?;
503 }
504 CsotXor => {
505 self.line("Operate Xor")?;
506 }
507 CsotLogicalAnd => {
508 self.line("Operate LAnd")?;
509 }
510 CsotLogicalOr => {
511 self.line("Operate LOr")?;
512 }
513 }
514 Ok(())
515 }
516
517 fn command_uni_operate(&mut self) -> Result<()> {
518 let csuot = self.read_csuot()?;
519 match csuot {
520 CsuotPlus => {
521 self.line("UnaryOperate Plus")?;
522 }
523 CsuotNegate => {
524 self.line("UnaryOperate Negate")?;
525 }
526 CsuotBitnot => {
527 self.line("UnaryOperate BitNot")?;
528 }
529 CsuotLogicalNot => {
530 self.line("UnaryOperate LNot")?;
531 }
532 }
533 Ok(())
534 }
535
536 fn command_compare(&mut self) -> Result<()> {
537 let csct = self.read_csct()?;
538 match csct {
539 CsctEqual => {
540 self.line("Compare Equal")?;
541 }
542 CsctNotEqual => {
543 self.line("Compare NotEqual")?;
544 }
545 CsctLessThan => {
546 self.line("Compare LessThan")?;
547 }
548 CsctLessEqual => {
549 self.line("Compare LessEqual")?;
550 }
551 CsctGreaterThan => {
552 self.line("Compare GreaterThan")?;
553 }
554 CsctGreaterEqual => {
555 self.line("Compare GreaterEqual")?;
556 }
557 }
558 Ok(())
559 }
560}